// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//  File: avtDataBinningFilter.C
// ************************************************************************* //

#include <avtDataBinningFilter.h>

#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkPointData.h>
#include <vtkRectilinearGrid.h>

#include <avtCallback.h>
#include <avtDataBinning.h>
#include <avtDataBinningConstructor.h>
#include <avtExtents.h>
#include <avtParallel.h>

#include <float.h>

#include <string>
#include <vector>

// ****************************************************************************
//  Method: avtDataBinningFilter constructor
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
// ****************************************************************************

avtDataBinningFilter::avtDataBinningFilter()
{
    varname = "operators/DataBinning";
}


// ****************************************************************************
//  Method: avtDataBinningFilter destructor
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
//  Modifications:
//
// ****************************************************************************

avtDataBinningFilter::~avtDataBinningFilter()
{
}


// ****************************************************************************
//  Method:  avtDataBinningFilter::Create
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
// ****************************************************************************

avtFilter *
avtDataBinningFilter::Create()
{
    return new avtDataBinningFilter();
}


// ****************************************************************************
//  Method:      avtDataBinningFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
// ****************************************************************************

void
avtDataBinningFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const DataBinningAttributes*)a;
}


// ****************************************************************************
//  Method: avtDataBinningFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtDataBinningFilter with the given
//      parameters would result in an equivalent avtDataBinningFilter.
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
// ****************************************************************************

bool
avtDataBinningFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(DataBinningAttributes*)a);
}


// ****************************************************************************
//  Function: RemoveUndefinedPointsFromCurve
//
//  Purpose:
//      Removes all bins from a 1D vtkRectilinearGrid that have the "emptyVal".
//
//  Programmer: Hank Childs
//  Creation:   January 4, 2013
//
// ****************************************************************************

void
RemoveUndefinedPointsFromCurve(vtkDataSet *ds, double emptyVal)
{
    int  i;

    // Make sure we have a valid mesh
    if (ds->GetDataObjectType() != VTK_RECTILINEAR_GRID)
        return;
    vtkRectilinearGrid *rg = (vtkRectilinearGrid *) ds;
    int dims[3];
    rg->GetDimensions(dims);
    if (dims[0] <= 1 || dims[1] != 1 || dims[2] != 1)
        return;

    // Get the scalars
    vtkDataArray *v = rg->GetPointData()->GetScalars();
    if (v == NULL)
        return;

    // See how many match
    int nvals = v->GetNumberOfTuples();
    int numValid = 0;
    for (i = 0 ; i < nvals ; i++)
    {
        if (v->GetTuple1(i) != emptyVal)
            numValid++;
    }

    if (numValid == nvals)
        return;

    dims[0] = numValid;
    rg->SetDimensions(dims);
    vtkDataArray *oldX = rg->GetXCoordinates();
    vtkDataArray *newX = vtkDataArray::CreateDataArray(oldX->GetDataType());
    newX->SetNumberOfTuples(numValid);

    vtkDataArray *newV = vtkDataArray::CreateDataArray(v->GetDataType());
    newV->SetNumberOfTuples(numValid);

    int curIdx = 0;
    for (i = 0 ; i < nvals ; i++)
    {
        if (v->GetTuple1(i) != emptyVal)
        {
            newV->SetTuple1(curIdx, v->GetTuple1(i));
            newX->SetTuple1(curIdx, oldX->GetTuple1(i));
            curIdx++;
        }
    }

    v->SetName("nameWeGiveSoWeCanRemoveIt");
    rg->GetPointData()->RemoveArray("nameWeGiveSoWeCanRemoveIt");
    rg->GetPointData()->SetScalars(newV);
    newV->Delete();

    rg->SetXCoordinates(newX);
    newX->Delete();
}

// ****************************************************************************
//  Method: avtDataBinningFilter::Execute
//
//  Purpose:
//      Calculates the data binning.
//
//  Returns:       The output dataset.
//
//  Programmer: hchilds -- generated by xml2avt
//  Creation:   Thu Aug 19 20:24:23 PST 2010
//
//  Modifications:
//
//    Hank Childs, Thu Aug 26 13:47:30 PDT 2010
//    Change extents names.
//
//    Hank Childs, Sun Oct 17 09:45:44 PDT 2010
//    Change the name of the variable we create.
//
//    Hank Childs, Tue Nov 30 20:13:43 PST 2010
//    Set the spatial extents of the output.  Otherwise downstream filters might
//    get confused.
//
//    Hank Childs, Mon Aug  1 07:13:23 PDT 2011
//    Add support for spatial dimensions.
//
//    Hank Childs, Mon Jul 16 17:22:00 PDT 2012
//    Add support for outputing on the input mesh.
//
//    Hank Childs, Fri Jan  4 11:49:36 PST 2013
//    Remove unused bins for curves.
//
// ****************************************************************************

void
avtDataBinningFilter::Execute(void)
{
    ConstructDataBinningAttributes dba = atts.CreateConstructionAtts();
    std::vector<double> bb = dba.GetBinBoundaries();
    if (! atts.GetDim1SpecifyRange())
    {
        if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Variable)
        {
            std::string v1name = atts.GetDim1Var();
            if (v1name == "default")
                v1name = pipelineVariable;
            double range[2];
            GetDataExtents(range, v1name.c_str());
            bb[0] = range[0];
            bb[1] = range[1];
        }
        else
        {
            int dim = (atts.GetDim1BinBasedOn()-DataBinningAttributes::X);
            double range[6];
            GetSpatialExtents(range);
            bb[0] = range[2*dim];
            bb[1] = range[2*dim+1];
        }
    }
    if ((! atts.GetDim2SpecifyRange()) && (atts.GetNumDimensions() == DataBinningAttributes::Two ||
                                         atts.GetNumDimensions() == DataBinningAttributes::Three))
    {
        if (atts.GetDim2BinBasedOn() == DataBinningAttributes::Variable)
        {
            std::string v2name = atts.GetDim2Var();
            if (v2name == "default")
                v2name = pipelineVariable;
            double range[2];
            GetDataExtents(range, v2name.c_str());
            bb[2] = range[0];
            bb[3] = range[1];
        }
        else
        {
            int dim = (atts.GetDim2BinBasedOn()-DataBinningAttributes::X);
            double range[6];
            GetSpatialExtents(range);
            bb[2] = range[2*dim];
            bb[3] = range[2*dim+1];
        }
    }
    if ((! atts.GetDim3SpecifyRange()) && atts.GetNumDimensions() == DataBinningAttributes::Three)
    {
        if (atts.GetDim3BinBasedOn() == DataBinningAttributes::Variable)
        {
            std::string v3name = atts.GetDim3Var();
            if (v3name == "default")
                v3name = pipelineVariable;
            double range[2];
            GetDataExtents(range, v3name.c_str());
            bb[4] = range[0];
            bb[5] = range[1];
        }
        else
        {
            int dim = (atts.GetDim3BinBasedOn()-DataBinningAttributes::X);
            double range[6];
            GetSpatialExtents(range);
            bb[4] = range[2*dim];
            bb[5] = range[2*dim+1];
        }
    }
    dba.SetBinBoundaries(bb);

    avtDataBinningConstructor dbc;
    dbc.SetInput(GetInput());
    avtDataBinning *d = dbc.ConstructDataBinning(&dba, lastContract, false);

    if (atts.GetOutputType() == DataBinningAttributes::OutputOnBins)
    {
        if (PAR_Rank() == 0)
        {
            vtkDataSet *ds = d->CreateGrid();
            bool isCurve = (atts.GetNumDimensions() == DataBinningAttributes::One);
            bool removeUndefined = atts.GetRemoveEmptyValFromCurve();
            if (isCurve && removeUndefined)
                RemoveUndefinedPointsFromCurve(ds, atts.GetEmptyVal());
            if (atts.GetNumDimensions() == DataBinningAttributes::One)
                ds->GetPointData()->GetScalars()->SetName(varname.c_str());
            else
                ds->GetCellData()->GetScalars()->SetName(varname.c_str());
            ds->GetCellData()->SetActiveScalars(varname.c_str());
            SetOutputDataTree(new avtDataTree(ds, -1));
            double range[2] = { DBL_MAX, -DBL_MAX };
            GetDataRange(ds, range, varname.c_str(), false);
            avtDataAttributes &dataAtts = GetOutput()->GetInfo().GetAttributes();
            dataAtts.GetThisProcsOriginalDataExtents(varname.c_str())->Set(range);
            dataAtts.GetThisProcsActualDataExtents(varname.c_str())->Set(range);
    
            ds->Delete();
        }
        else
            SetOutputDataTree(new avtDataTree());
     
        avtDataAttributes &dataAtts = GetOutput()->GetInfo().GetAttributes();
        dataAtts.GetThisProcsOriginalSpatialExtents()->Set(&bb[0]);
        dataAtts.GetOriginalSpatialExtents()->Set(&bb[0]);
    }
    else if (atts.GetOutputType() == DataBinningAttributes::OutputOnInputMesh)
    {
        double range[2] = { DBL_MAX, -DBL_MAX };
        bool hadError = false;
        avtDataTree_p tree = CreateArrayFromDataBinning(GetInputDataTree(), d, range, hadError);
        if (UnifyMaximumValue((int) hadError) > 0)
        {
            avtCallback::IssueWarning("The data binning could not be placed on the input "
                    "mesh.  This is typically because the data binning is over different "
                    "centerings (zonal and nodal) and can not be meaningfully placed back "
                    "on the input mesh.  Try recentering one of the variables to remove "
                    "this ambiguity.");
            SetOutputDataTree(new avtDataTree());
        }
        else
        {
            SetOutputDataTree(tree);
            avtDataAttributes &dataAtts = GetOutput()->GetInfo().GetAttributes();
            dataAtts.GetThisProcsOriginalDataExtents(varname.c_str())->Set(range);
            dataAtts.GetThisProcsActualDataExtents(varname.c_str())->Set(range);
        }
    }
    
    delete d;
}


// ****************************************************************************
//  Method: avtDataBinningFilter::CreateArrayFromDataBinning
//
//  Purpose:
//      Used for the mode where the data binning is applied on the input mesh.
//      This makes recursion easy.
//
//  Programmer: Hank Childs
//  Creation:   July 16, 2012
//
// ****************************************************************************

avtDataTree_p
avtDataBinningFilter::CreateArrayFromDataBinning(avtDataTree_p tree, 
                                                 avtDataBinning *theDataBinning, 
                                                 double *extents, bool &hadError)
{
    if (*tree == NULL)
        return NULL;

    int nc = tree->GetNChildren();

    if (nc <= 0 && !tree->HasData())
        return NULL;

    if (nc == 0)
    {
        //
        // there is only one dataset to process
        //
        vtkDataSet *in_ds = tree->GetDataRepresentation().GetDataVTK();
        vtkDataArray *res = theDataBinning->ApplyFunction(in_ds);
        if (res == NULL)
        {
            hadError = true;
            return NULL;
        }
        res->SetName(varname.c_str());
        if (res->GetNumberOfTuples() == in_ds->GetNumberOfCells())
        {
            in_ds->GetCellData()->AddArray(res);
            in_ds->GetCellData()->SetActiveScalars(varname.c_str());
        }
        if (res->GetNumberOfTuples() == in_ds->GetNumberOfPoints())
        {
            in_ds->GetPointData()->AddArray(res);
            in_ds->GetPointData()->SetActiveScalars(varname.c_str());
        }
        res->Delete();
        double range[2] = { DBL_MAX, -DBL_MAX };
        GetDataRange(in_ds, range, varname.c_str(), false);
        extents[0] = (extents[0] > range[0] ? range[0] : extents[0]);
        extents[1] = (extents[1] < range[1] ? range[1] : extents[1]);

        int dom = tree->GetDataRepresentation().GetDomain();
        std::string label = tree->GetDataRepresentation().GetLabel();
        avtDataTree_p rv = new avtDataTree(in_ds, dom, label);
        return rv;
    }
    else
    {
        avtDataTree_p *outDT = new avtDataTree_p[nc];
        for (int j = 0; j < nc; j++)
        {
            if (tree->ChildIsPresent(j))
                outDT[j] = CreateArrayFromDataBinning(tree->GetChild(j), 
                                theDataBinning, extents, hadError);
            else
                outDT[j] = NULL;
        }
        avtDataTree_p rv = new avtDataTree(nc, outDT);
        delete [] outDT;
        return (rv);
    }
}


// ****************************************************************************
//  Method: avtDataBinningFilter::ModifyContract
//
//  Purpose:
//      Sets up the contract to have the requisite variables.
//
//  Programmer: Hank Childs
//  Creation:   August 19, 2010
//
//  Modifications:
//
//    Hank Childs, Tue Aug 31 10:20:08 PDT 2010
//    Change names of added variable.
//
//    Hank Childs, Tue Aug 31 13:28:48 PDT 2010
//    Beef up error messages.
//
// ****************************************************************************

avtContract_p
avtDataBinningFilter::ModifyContract(avtContract_p inContract)
{
    bool defaultVarOK = true;
    if (strncmp(pipelineVariable, "operators/DataBinning", strlen("operators/DataBinning")) == 0)
    {
        defaultVarOK = false;
        varname = pipelineVariable;
    }

    const char *dim1Var = atts.GetDim1Var().c_str();
    if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Variable)
    {
        if (strcmp(dim1Var, "default") == 0)
        {    
            if (defaultVarOK)
                dim1Var = pipelineVariable;
            else
            {
                EXCEPTION1(VisItException, "You specified the first dimension of the "
                                   "data binning as \"default\", but your plotting variable "
                                   "(which \"default\" resolves to) is of the output of the "
                                   "data binning.  This is a recursion definition.  Please "
                                   "change the first dimension of the data binning to be "
                                   "something besides \"default\".");
            }
        }
    }

    const char *dim2Var = atts.GetDim2Var().c_str();
    if (atts.GetDim2BinBasedOn() == DataBinningAttributes::Variable)
    {
        if (strcmp(dim2Var, "default") == 0 && 
            (atts.GetNumDimensions() == DataBinningAttributes::Two || 
             atts.GetNumDimensions() == DataBinningAttributes::Three))
        {    
            if (defaultVarOK)
                dim2Var = pipelineVariable;
            else
            {
                EXCEPTION1(VisItException, "You specified the second dimension of the "
                                   "data binning as \"default\", but your plotting variable "
                                   "(which \"default\" resolves to) is of the output of the "
                                   "data binning.  This is a recursion definition.  Please "
                                   "change the first dimension of the data binning to be "
                                   "something besides \"default\".");
            }
        }
    }

    const char *dim3Var = atts.GetDim3Var().c_str();
    if (atts.GetDim3BinBasedOn() == DataBinningAttributes::Variable)
    {
        if (strcmp(dim3Var, "default") == 0 && 
            atts.GetNumDimensions() == DataBinningAttributes::Three)
        {    
            if (defaultVarOK)
                dim3Var = pipelineVariable;
            else
            {
                EXCEPTION1(VisItException, "You specified the third dimension of the "
                                   "data binning as \"default\", but your plotting variable "
                                   "(which \"default\" resolves to) is of the output of the "
                                   "data binning.  This is a recursion definition.  Please "
                                   "change the first dimension of the data binning to be "
                                   "something besides \"default\".");
            }
        }
    }

    avtDataRequest_p in_dr  = inContract->GetDataRequest();
    avtDataRequest_p out_dr;
    avtDataAttributes &inAtts   = GetInput()->GetInfo().GetAttributes();
    if (strncmp(in_dr->GetVariable(), "operators/DataBinning", strlen("operators/DataBinning")) == 0)
        out_dr = new avtDataRequest(in_dr, inAtts.GetMeshname().c_str());
    else
        out_dr = new avtDataRequest(in_dr);
    std::vector<CharStrRef>   vars2nd = in_dr->GetSecondaryVariablesWithoutDuplicates();
    std::vector<std::string>  removeMe;
    for (size_t i = 0 ; i < vars2nd.size() ; i++)
        if (strncmp(*(vars2nd[i]), "operators/DataBinning", strlen("operators/DataBinning")) == 0)
        {
            varname = *(vars2nd[i]);
            removeMe.push_back(*(vars2nd[i]));
        }
    for (size_t i = 0 ; i < removeMe.size() ; i++)
        out_dr->RemoveSecondaryVariable(removeMe[i].c_str());

    if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Variable)
        out_dr->AddSecondaryVariable(dim1Var);
    if ((atts.GetNumDimensions() == DataBinningAttributes::Two || 
        atts.GetNumDimensions() == DataBinningAttributes::Three)
        && (atts.GetDim2BinBasedOn() == DataBinningAttributes::Variable))
        out_dr->AddSecondaryVariable(dim2Var);
    if ((atts.GetNumDimensions() == DataBinningAttributes::Three)
        && (atts.GetDim3BinBasedOn() == DataBinningAttributes::Variable))
        out_dr->AddSecondaryVariable(dim3Var);
    if (atts.GetReductionOperator() != DataBinningAttributes::PDF && 
        atts.GetReductionOperator() != DataBinningAttributes::Count)
    {
        if (atts.GetVarForReduction() == "default")
        {
            if (defaultVarOK)
                out_dr->AddSecondaryVariable(pipelineVariable);
            else
            {
                EXCEPTION1(VisItException, "You specified the variable for the reduction operator of your "
                               "data binning as \"default\", but your plotting variable "
                               "(which \"default\" resolves to) is of the output of the "
                               "data binning.  This is a recursion definition.  Please "
                               "change the first dimension of the data binning to be "
                               "something besides \"default\".");
            }
        }
        else
            out_dr->AddSecondaryVariable(atts.GetVarForReduction().c_str());
    }

    //
    // Calculate the "original" extents.  If we clip away part of the volume, we don't
    // want the extents bouncing all around because certain regions aren't contributing.
    //
    avtContract_p rv = new avtContract(inContract, out_dr);
    if (! atts.GetDim1SpecifyRange() && dim1Var != pipelineVariable
        && atts.GetDim1BinBasedOn() == DataBinningAttributes::Variable)
        rv->SetCalculateVariableExtents(dim1Var, true);
    if ((! atts.GetDim2SpecifyRange()) && (atts.GetNumDimensions() == DataBinningAttributes::Two ||
                                         atts.GetNumDimensions() == DataBinningAttributes::Three)
         && (dim2Var != pipelineVariable)
         && (atts.GetDim2BinBasedOn() == DataBinningAttributes::Variable))
        rv->SetCalculateVariableExtents(dim2Var, true);
    if ((! atts.GetDim3SpecifyRange()) && atts.GetNumDimensions() == DataBinningAttributes::Three
         && (dim2Var != pipelineVariable)
         && (atts.GetDim3BinBasedOn() == DataBinningAttributes::Variable))
        rv->SetCalculateVariableExtents(dim3Var, true);

    int numSpatialDimensions = 0;
    if ((atts.GetDim1BinBasedOn() != DataBinningAttributes::Variable) &&
        (! atts.GetDim1SpecifyRange()))
        numSpatialDimensions++;
    if ((atts.GetDim2BinBasedOn() != DataBinningAttributes::Variable) 
        && (! atts.GetDim2SpecifyRange()) 
        && (atts.GetNumDimensions() == DataBinningAttributes::Two ||
            atts.GetNumDimensions() == DataBinningAttributes::Three))
        numSpatialDimensions++;
    if ((atts.GetDim3BinBasedOn() != DataBinningAttributes::Variable) 
        && (! atts.GetDim3SpecifyRange()) 
        && (atts.GetNumDimensions() == DataBinningAttributes::Three))
        numSpatialDimensions++;

    if (numSpatialDimensions > 0)
        rv->SetCalculateMeshExtents(true);
    
    lastContract = rv;

    return rv;
}


// ****************************************************************************
//  Method: avtDataBinningFilter::UpdateDataObjectInfo
//
//  Purpose:
//      Changes the info to reflect the new dimensions.
//
//  Programmer: Hank Childs
//  Creation:   August 19, 2010
//
//  Modifications:
//
//    Hank Childs, Thu Aug 26 13:47:30 PDT 2010
//    Change extents names.
//
//    Hank Childs, Tue Aug 31 10:20:08 PDT 2010
//    Set up output labels.
//
//    Hank Childs, Sun Oct 17 09:45:44 PDT 2010
//    Change the name of the variable we create.
//
//    Cyrus Harrison, Tue Feb 15 13:44:45 PST 2011
//    Preserve units if possible & better y-axis label for 1D case.
//
//    Hank Childs, Wed Sep 21 09:15:21 PDT 2011
//    Fix problem where 3D unstructured meshes sometimes had exceptions.
//
//    Hank Childs, Tue Jul 10 09:47:04 PDT 2012
//    Set the labels correctly when using spatial coordinates.
//
//    Hank Childs, Mon Jul 16 17:22:00 PDT 2012
//    Split logic for setting axis names and units into its own method.
//
//    Brad Whitlock, Mon Apr  7 15:55:02 PDT 2014
//    Add filter metadata used in export.
//    Work partially supported by DOE Grant SC0007548.
//
// ****************************************************************************

void
avtDataBinningFilter::UpdateDataObjectInfo(void)
{
    avtDataAttributes &dataAtts = GetOutput()->GetInfo().GetAttributes();
    dataAtts.AddVariable(varname);
    dataAtts.SetActiveVariable(varname.c_str());
    dataAtts.SetVariableDimension(1);
    dataAtts.SetVariableType(AVT_SCALAR_VAR);

    if (atts.GetOutputType() == DataBinningAttributes::OutputOnBins)
    {
        if (atts.GetNumDimensions() == DataBinningAttributes::One)
            dataAtts.SetCentering(AVT_NODECENT);
        else 
            dataAtts.SetCentering(AVT_ZONECENT);
        SetAxisNamesAndUnits();
    }

    dataAtts.AddFilterMetaData("DataBinning");
}


// ****************************************************************************
//  Method: avtDataBinningFilter::SetAxisNamesAndUnits
//
//  Purpose:
//      Sets the names of the axes and their units when creating bin outputs.
//
//  Programmer: Hank Childs
//  Creation:   July 16, 2012
//
// ****************************************************************************

void
avtDataBinningFilter::SetAxisNamesAndUnits(void)
{
    avtDataAttributes &inAtts   = GetInput()->GetInfo().GetAttributes();
    avtDataAttributes &dataAtts = GetOutput()->GetInfo().GetAttributes();
    int dim = ( (atts.GetNumDimensions() == DataBinningAttributes::One) ? 1
              : ((atts.GetNumDimensions() == DataBinningAttributes::Two) ? 2 : 3));
    {
        dataAtts.SetTopologicalDimension(dim);
        dataAtts.SetSpatialDimension(dim);
        dataAtts.GetThisProcsOriginalSpatialExtents()->Clear();
        dataAtts.GetOriginalSpatialExtents()->Clear();
        dataAtts.GetDesiredSpatialExtents()->Clear();
    }

    if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Variable)
    {
        std::string var1 = "";
        if (atts.GetDim1Var() == "default")
        {
            if (pipelineVariable != NULL)
                var1 = pipelineVariable;
        }
        else
            var1 = atts.GetDim1Var();
        dataAtts.SetXLabel(var1);
        if(inAtts.ValidVariable(var1.c_str()))
            dataAtts.SetXUnits(inAtts.GetVariableUnits(var1.c_str()));
        else
            dataAtts.SetXUnits("");
    }
    else if (atts.GetDim1BinBasedOn() == DataBinningAttributes::X)
    {
        dataAtts.SetXLabel("X");
        dataAtts.SetXUnits(inAtts.GetXUnits());
    }
    else if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Y)
    {
        dataAtts.SetXLabel("Y");
        dataAtts.SetXUnits(inAtts.GetYUnits());
    }
    else if (atts.GetDim1BinBasedOn() == DataBinningAttributes::Z)
    {
        dataAtts.SetXLabel("Z");
        dataAtts.SetXUnits(inAtts.GetZUnits());
    }

    if (atts.GetNumDimensions() == DataBinningAttributes::One)
    {
        // In this case we generate a curve, so create a sensible
        // axis name for the output var.
        DataBinningAttributes::ReductionOperator rop_id = atts.GetReductionOperator();
        std::string rop_str = DataBinningAttributes::ReductionOperator_ToString(rop_id);
        if( ! (rop_id == DataBinningAttributes::Count ||
               rop_id == DataBinningAttributes::RMS ||
               rop_id == DataBinningAttributes::PDF) )
        {
            rop_str = rop_str + "(" + atts.GetVarForReduction() + ")";
        }
        dataAtts.SetYLabel(rop_str);
        dataAtts.SetYUnits("");
    }

    if ((atts.GetNumDimensions() == DataBinningAttributes::Two ||
         atts.GetNumDimensions() == DataBinningAttributes::Three))
    {
        if (atts.GetDim2BinBasedOn() == DataBinningAttributes::Variable)
        {
            std::string var2 = "";
            if (atts.GetDim2Var() == "default")
            {
                if (pipelineVariable != NULL)
                    var2 = pipelineVariable;
            }
            else
                var2 = atts.GetDim2Var();

            dataAtts.SetYLabel(var2);
            dataAtts.SetYUnits("");
            if(inAtts.ValidVariable(var2.c_str()))
                dataAtts.SetYUnits(inAtts.GetVariableUnits(var2.c_str()));
            else
                dataAtts.SetYUnits("");
        }
        else if (atts.GetDim2BinBasedOn() == DataBinningAttributes::X)
        {
            dataAtts.SetYLabel("X");
            dataAtts.SetYUnits(inAtts.GetXUnits());
        }
        else if (atts.GetDim2BinBasedOn() == DataBinningAttributes::Y)
        {
            dataAtts.SetYLabel("Y");
            dataAtts.SetYUnits(inAtts.GetYUnits());
        }
        else if (atts.GetDim2BinBasedOn() == DataBinningAttributes::Z)
        {
            dataAtts.SetYLabel("Z");
            dataAtts.SetYUnits(inAtts.GetZUnits());
        }

    }

    if (atts.GetNumDimensions() == DataBinningAttributes::Three)
    {
        if (atts.GetDim3BinBasedOn() == DataBinningAttributes::Variable)
        {
            std::string var3 = "";
            if (atts.GetDim3Var() == "default")
            {
                if (pipelineVariable != NULL)
                    var3 = pipelineVariable;
            }
            else
                var3 = atts.GetDim3Var();
            dataAtts.SetZLabel(var3);
            if(inAtts.ValidVariable(var3.c_str()))
                dataAtts.SetZUnits(inAtts.GetVariableUnits(var3.c_str()));
            else
                dataAtts.SetZUnits("");
        }
        else if (atts.GetDim3BinBasedOn() == DataBinningAttributes::X)
        {
            dataAtts.SetZLabel("X");
            dataAtts.SetZUnits(inAtts.GetXUnits());
        }
        else if (atts.GetDim3BinBasedOn() == DataBinningAttributes::Y)
        {
            dataAtts.SetZLabel("Y");
            dataAtts.SetZUnits(inAtts.GetYUnits());
        }
        else if (atts.GetDim3BinBasedOn() == DataBinningAttributes::Z)
        {
            dataAtts.SetZLabel("Z");
            dataAtts.SetZUnits(inAtts.GetZUnits());
        }
    }

    GetOutput()->GetInfo().GetValidity().InvalidateZones();
}


